/**
 * Copyright (C) 2012 Daniel Gee.
 * See LICENSE.txt for more details.
 */
package examples;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import com.rl.dungeon.Direction;
import com.rl.dungeon.DungeonTerrain;
import com.rl.dungeon.Location;
import com.rl.dungeon.fov.VisionCheck;
import com.rl.dungeon.pathing.MoveCostMap;
import com.rl.gui.Appearance;
import com.rl.gui.AppearanceMap;

/**
 * A very simplistic dungeon level implementation.
 * 
 * @author Daniel Gee
 */
public class SimpleDungeon implements AppearanceMap, MoveCostMap, VisionCheck
{
	/**
	 * 2d Array of map contents; indexed with [x][y].
	 */
	private DungeonTerrain[][] mapCells;

	/**
	 * Creates a SimpleDungeon based on the tiles given.
	 * 
	 * @param tiles
	 *            the tiles of the new dungeon.
	 */
	public SimpleDungeon(DungeonTerrain[][] tiles)
	{
		this.mapCells = tiles;
	}

	/**
	 * Creates a new blank SimpleDungeon with the size specified.
	 * 
	 * @param width
	 *            how wide the dungeon should be.
	 * @param height
	 *            how tall the dungeon should be.
	 */
	public SimpleDungeon(int width, int height)
	{
		this.mapCells = new DungeonTerrain[width][height];
		for (int x = 0; x < width; x++)
		{
			for (int y = 0; y < height; y++)
			{
				this.mapCells[x][y] = DungeonTerrain.Wall;
			}
		}
	}

	/**
	 * @see com.rl.gui.AppearanceMap#getAppearanceAt(com.rl.dungeon.Location)
	 */
	@Override
	public Appearance getAppearanceAt(Location location)
	{
		return this.getTileTypeAt(location).appearance;
	}

	/**
	 * @see com.rl.gui.AppearanceMap#getDisplayStartLocation(int, int)
	 */
	@Override
	public Location getDisplayStartLocation(int displayWidth, int displayHeight)
	{
		return Location.of(0, 10, 0);
	}

	/**
	 * @return the dungeon height
	 */
	public int getHeight()
	{
		return this.mapCells[0].length;
	}

	/**
	 * @see com.rl.dungeon.pathing.MoveCostMap#getLocationsAccessableFrom(com.rl.dungeon.Location)
	 */
	@Override
	public Collection<Location> getLocationsAccessableFrom(Location location)
	{
		Set<Location> retVal = new HashSet<>();
		for (Direction d : Direction.laterals())
		{
			Location loc = d.from(location);
			if (this.getTileTypeAt(loc).isWalkable())
			{
				retVal.add(loc);
			}
		}
		return retVal;
	}

	/**
	 * @see com.rl.dungeon.pathing.MoveCostMap#getMoveCostAt(com.rl.dungeon.Location)
	 */
	@Override
	public int getMoveCostAt(Location location)
	{
		return 1;
	}

	/**
	 * @return the location of the stairs, or null if there are none.
	 */
	public Location getStairs()
	{
		for (int x = 0; x < this.getWidth(); x++)
		{
			for (int y = 0; y < this.getHeight(); y++)
			{
				if (this.mapCells[x][y] == DungeonTerrain.StairsDown)
				{
					return Location.of(x, y);
				}
			}
		}
		return null;
	}

	/**
	 * Obtains the type of tile that's at the location specified.
	 * 
	 * @param location
	 *            the location in question
	 * 
	 * @return the type of tile at this location.
	 */
	public DungeonTerrain getTileTypeAt(Location location)
	{
		if (location.x < 0 || location.y < 0 || location.z < 0 || location.x >= this.getWidth()
				|| location.y >= this.getHeight() || location.z >= 1)
		{
			return DungeonTerrain.Void;
		}
		else
		{
			return this.mapCells[location.x][location.y];
		}
	}

	/**
	 * @return the dungeon width
	 */
	public int getWidth()
	{
		return this.mapCells.length;
	}

	/**
	 * @return this dungeon's edge.
	 */
	public Location getEdge()
	{
		return Location.of(this.getWidth(), this.getHeight());
	}

	/**
	 * @see com.rl.dungeon.pathing.MoveCostMap#isWalkable(com.rl.dungeon.Location)
	 */
	@Override
	public boolean isWalkable(Location location)
	{
		return getTileTypeAt(location).isWalkable();
	}

	/**
	 * Assigns a new tile type to the location specified.
	 * 
	 * @param x
	 *            the x location.
	 * @param y
	 *            the y location.
	 * @param type
	 *            the new type of tile at that location.
	 */
	public void setTileTypeAt(int x, int y, DungeonTerrain type)
	{
		this.mapCells[x][y] = type;
	}

	/**
	 * @see com.rl.dungeon.fov.VisionCheck#visionBlocked(Location)
	 */
	@Override
	public boolean visionBlocked(Location loc)
	{
		return this.mapCells[loc.x][loc.y].isVisionBlocking();
	}
}
